/*------------------------------------------------------------------------
|                            FILE DESCRIPTION                            |
------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
|  - File name     : os_task.c
|  - Author        : zeweni
|  - Update date   : 2021.03.25
|  - Copyright(C)  : 2021-2021 zeweni. All rights reserved.
------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
|                            COPYRIGHT NOTICE                            |
------------------------------------------------------------------------*/
/*
 * Copyright (C) 2021, zeweni (17870070675@163.com)

 * This file is part of Ant Real Time Operating System.

 * Ant Real Time Operating System is free software: you can redistribute 
 * it and/or modify it under the terms of the Apache-2.0 License.

 * Ant Real Time Operating System is distributed in the hope that it will 
 * be useful,but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * Apache-2.0 License License for more details.

 * You should have received a copy of the Apache-2.0 License.Ant Real Time
 * Operating System. If not, see <http://www.apache.org/licenses/>.
**/
/*------------------------------------------------------------------------
|                                INCLUDES                                |
------------------------------------------------------------------------*/

#include "os_port.h"

#include "heap_os.h"

/*------------------------------------------------------------------------
|                                  DATA                                  |
------------------------------------------------------------------------*/

/**
 * @brief   正在运行的任务。
 * @details Tasks that are running.
**/
OS_TCB_t xdata * G_TCB_RunNode = NULL;

/**
 * @brief   就绪任务链表。
 * @details List of ready tasks.
**/
TCB_RdyList_t  G_TCB_RdyList;

/**
 * @brief   阻塞任务链表。
 * @details List os blocked tasks.
**/
TCB_BlkList_t G_TCB_BlkList;

/**
 * @brief   休眠任务链表。
 * @details List os slpeeing tasks.
**/
TCB_SlpList_t G_TCB_SlpList;

/**
 * @brief   被创建任务的当前数量。
 * @details The current number of tasks being created.
**/
static uint8_t G_Task_CrtNum = 0;

/*------------------------------------------------------------------------
|                              API FUNCTIONS                             |
------------------------------------------------------------------------*/


/**
 * @brief   Create task thread,where in the rtos kernel static memory bolcks.
 * @brief   Because using dynamic memory blocks requires more code and time.
 * @param   pTaskFun [IN] Task function address.
 * @param   tskID [IN] Task id.
 * @param   tskPrio [IN] Task priority,
 *          creating tasks of the same priority is not supported. 
 * @param   *pStk [IN] Task stack address.
 * @param   stkSize [IN] Task stack size.
 * @retval  [OS_TCB_t *] Create Task success,task tcb handle returned.
 * @retval  [NULL] Create Task fail.
**/
os_uint8_t OS_Task_Create(void(*entry)(void), os_uint8_t tskPrio, os_uint8_t *pStk, os_uint8_t stkSize)
{
    /* Define a task control bolck list node pointer. */
    OS_TCB_t *tcbNode = NULL;
    
	
    /* Define an unsigned char variable. */		
    os_uint8_t num = 0;
	
    /* RTOS enters the critical area of the system. */		
    OS_ENTER_CRITICAL();
	
    /* Created number of tasks have to less than max number of tasks.
     * If the condition is not satisfied, NOK is returned. */
    if(G_Task_CrtNum <= OS_TASK_MAX_NUM)
    {
        /* Alloced memory for tcbNode. If alloc fail, NOK is returned. */
        OS_TCB_MALLOC(tcbNode);
		
        if(tcbNode != NULL)
        {

            /* Read TCB information. */			
           
            tcbNode -> TaskPrio = tskPrio;   
            tcbNode -> StkAddr = pStk; 
            tcbNode -> StkSize = stkSize;
			tcbNode -> TaskSlp = 0;
            tcbNode -> TaskState = OSTask_Ready;
			
            /* The bottom of the stack holds the length of the data 
             * to be filled in the stack. */
			*(pStk++) = 15; /* 15 = 2 (task function address) + 13 (mcu register data) */
			
            /* The second address read task function low 8 bits address.
			 * The third address read task function high 8 bits address. */					
			*pStk++ = (os_uint16_t)entry;
			*pStk = (os_uint16_t)entry >> 8;
			
			/* The fourth to sixteenth stack positions are cleared first,
			 * and the position here is to save MCU registers.	*/
			for(num=0;num<13;num++) *(++pStk) = 0;
			
			/* If ready list is not empty */
			for(num=0;num<G_TCB_RdyList.RdyNum;num++)
			{
				if(G_TCB_RdyList.RdyPtr[num] -> TaskPrio == tcbNode -> TaskPrio)
				{
					OS_EXIT_CRITICAL();
					return OS_ERROR;
				}
			}
			
			G_TCB_RdyList.RdyPtr[G_TCB_RdyList.RdyNum++] = tcbNode;
			tcbNode -> TaskID = G_Task_CrtNum++; 

			/* If the address of the running node is not null, 
			 * it proves that there is a task running, 
			 * and the task scheduling occurs immediately 
			 * to ensure the real-time performance of the system. */
			if(G_TCB_RunNode != NULL) OS_Context_Schedule();
			OS_EXIT_CRITICAL();
			return tcbNode -> TaskID;
		}
	}
	
	/* When running to this location, 
	 * it proves that the task creation fails, 
	 * exits the critical area of the system, and returns an error code. */
	OS_EXIT_CRITICAL();
	return OS_ERROR;
}	

/**
 * @brief   Delete task threads.
 * @param   tskID [IN] Task id.
 * @retval  [OS_OK] Task deleted successfully.
 * @retval  [OS_NOK] Task deleted failed.
**/
os_uint8_t OS_Task_Delete(os_uint8_t tskID)
{
	extern OS_TCB_Memory_t xdata G_OS_TCB_Memory;
	os_uint8_t num = 0,maxPrio = 0,maxPrioCtr = 0;
    /* Define a task control bolck list node pointer. */
    OS_TCB_t *tcbNode = NULL;
    
	/* RTOS enters the critical area of the system. */	
	OS_ENTER_CRITICAL();
	
	/* If the task id is equal to the runing node id, 
	 * The runing node clean.
	 * RTOS extis the critical area of the system.
	 * RTOS switch context at the same time.  */	
	if(tskID == G_TCB_RunNode -> TaskID || tskID == TASK_SELF)
	{
		G_Task_CrtNum--;
		
		G_OS_TCB_Memory.Block[tskID].TaskID = G_OS_TCB_Memory.Block[G_Task_CrtNum].TaskID;
		G_OS_TCB_Memory.Block[tskID].TaskPrio = G_OS_TCB_Memory.Block[G_Task_CrtNum].TaskPrio;
		G_OS_TCB_Memory.Block[tskID].TaskState = G_OS_TCB_Memory.Block[G_Task_CrtNum].TaskState;
		G_OS_TCB_Memory.Block[tskID].TaskSlp = G_OS_TCB_Memory.Block[G_Task_CrtNum].TaskSlp;
		G_OS_TCB_Memory.Block[tskID].StkAddr = G_OS_TCB_Memory.Block[G_Task_CrtNum].StkAddr;
		G_OS_TCB_Memory.Block[tskID].StkSize = G_OS_TCB_Memory.Block[G_Task_CrtNum].StkSize;
		
		G_TCB_RunNode = NULL;
		
		for(num=0;num < G_TCB_RdyList.RdyNum;num++)
		{
			if (G_TCB_RdyList.RdyPtr[num] -> TaskPrio > maxPrio)
			{
				maxPrio = G_TCB_RdyList.RdyPtr[num] -> TaskPrio;
				maxPrioCtr = num;
			}
		}
		
		tcbNode = G_TCB_RdyList.RdyPtr[0];
		G_TCB_RdyList.RdyPtr[0] = G_TCB_RdyList.RdyPtr[maxPrioCtr];
		G_TCB_RdyList.RdyPtr[maxPrioCtr] = tcbNode;
		
		OS_Context_Schedule();
		
		return OS_OK;
	}
	else
	{
		
	}
		
	/* When running to this location, 
	 * it delete that the task creation fails, 
	 * exits the critical area of the system, and returns an error code. */	
	OS_EXIT_CRITICAL();
	return OS_NOK;
}

/**
  * @name    OS_Task_Sleep
  * @brief   Delete task threads.
  * @param   ticks [IN] Task sleep ticks.
  * @retval  None.
***/
void OS_Task_Sleep(os_uint16_t ticks)
{
	os_uint8_t num = 0;
	os_uint8_t maxPrio = 0,maxPrioCtr=0;
	OS_TCB_t *tcbNode = NULL;
	
	/* RTOS enters the critical area of the system. */	
	OS_ENTER_CRITICAL();
	
	G_TCB_RunNode -> TaskSlp = ticks;
	G_TCB_RunNode -> TaskState = OSTask_Sleep;
	
	/* RTOS extis the critical area of the system.
	 * RTOS switch context at the same time.  */	

	for(num=0;num < G_TCB_RdyList.RdyNum;num++)
	{
		
		if (G_TCB_RdyList.RdyPtr[num] -> TaskPrio > maxPrio)
		{
			maxPrio = G_TCB_RdyList.RdyPtr[num] -> TaskPrio;
			maxPrioCtr = num;
		}
	}
	
	tcbNode = G_TCB_RdyList.RdyPtr[0];
	G_TCB_RdyList.RdyPtr[0] = G_TCB_RdyList.RdyPtr[maxPrioCtr];
	G_TCB_RdyList.RdyPtr[maxPrioCtr] = tcbNode;
	
	OS_Context_Schedule();
	
}


/*------------------------------------------------------------------------
|                    END OF FLIE.  (C) COPYRIGHT zeweni                  |
------------------------------------------------------------------------*/